#include "stdafx.h"
#include "_YAxis.h"
#include <math.h>
#include "_GraphLayout.h"

/**
*  @brief Constructor
*/
_YAxis::_YAxis(_GraphLayout& Layout):
    _Axis(Layout)
{
    m_Color = RGB(0xC0, 0xC0, 0xC0);
    m_BaseSize = 30;
}

/**
*  @brief Destructor
*/
_YAxis::~_YAxis(void)
{
}


/**
*  @brief Draws the title of the axis
*/
void _YAxis::drawTitle()
{
    _u32 YAxisHeight = m_Layout.m_ControlRect.Height() - m_Layout.m_XAxisHeight - 2 * m_Layout.m_BorderSize -
                       m_Layout.m_TitleHeight;
    glViewport(m_Layout.m_BorderSize,                    // X0
               m_Layout.m_BorderSize + m_Layout.m_XAxisHeight, // Y0
               AxisTitleSize,              // Width
               YAxisHeight                 // Height
              );
    glLoadIdentity();
    gluOrtho2D(0, 1, 0, YAxisHeight);

    // Draw axis title
    if(isTitleVisible())
    {
        CStringW Temp;

        for(int i = m_Title.GetLength() - 1; i >= 0; i--)
        {
            // Draw text vertically
            Temp.Format(L"%c", m_Title.GetAt(i));
            drawText(Temp, OGL_DRAWABLE_FONT_LUCIDA_CONSOLE_10, 0.5, YAxisHeight / 2 + 10 * wcslen(m_Title) / 2 - 10 * i, 0, 0, 0);
        }
    }
}

/**
*  @brief Draws the background of the axis
*  @param m_Layout.m_BorderSize  - Size of the border
*  @param m_Layout.m_TitleHeight   - Size of the title area
*  @param m_Layout.m_XAxisHeight - Height of the X-Axis area
*  @param m_Exponent    - m_Exponent of the shown numbers
*/
void _YAxis::drawBackground()
{
    glColor3i(GET_GLINT_R(m_Color), GET_GLINT_G(m_Color), GET_GLINT_B(m_Color));
    glViewport(m_Layout.m_BorderSize,                                                           // X0
               m_Layout.m_BorderSize + m_Layout.m_XAxisHeight,                                         // Y0
               m_BaseSize + AxisTitleSize + m_Precision * 5,                       // Width
               m_Layout.m_ControlRect.Height() - m_Layout.m_XAxisHeight - 2 * m_Layout.m_BorderSize -
               m_Layout.m_TitleHeight      // Height
              );
    // Fill complete viewport with color
    glLoadIdentity();
    gluOrtho2D(0, 1, 0, 1);
    glRectd(0, 0, 1, 1);
}


/**
*  @brief Draws the ticks and tickstrings
*  @param m_Layout.m_BorderSize  - Size of the border
*  @param m_Layout.m_TitleHeight   - Size of the title area
*  @param m_Layout.m_XAxisHeight - Height of the X-Axis area
*  @param m_Exponent    - m_Exponent of the shown numbers
*/
void _YAxis::drawTicks(bool EnableGrid)
{
    glColor3i(0, 0, 0);
    _u32 Ticks = (_u32)(m_Range / m_TickSpacing);

    // Workaround empty axis
    if(Ticks < 3)
    {
        m_TickSpacing /= 2;
        Ticks *= 2;
    }

    _u32 AxisMainWidth = m_BaseSize + (m_Precision) * 6;
    _u32 AxisMainHeight = m_Layout.m_ControlRect.Height() - m_Layout.m_XAxisHeight - 2 * m_Layout.m_BorderSize -
                          m_Layout.m_TitleHeight;
    glViewport(m_Layout.m_BorderSize + AxisTitleSize,       // X0
               m_Layout.m_BorderSize + m_Layout.m_XAxisHeight,     // Y0
               m_Layout.m_ControlRect.Width(),                     // Width
               AxisMainHeight                                      // Height
              );
    glLoadIdentity();
    glOrtho(0, m_Layout.m_ControlRect.Width(), 0, 1, -1, 1);
    double Offset = m_NiceMin;
    double Range  = m_NiceMax - m_NiceMin;
    // Draw the ticks on the right side of the axis
    double CurrentTick = m_FirstTickPosition + m_TickSpacing;
    CStringW TickString = L"";
    CStringW FormatString = L"";
    FormatString.Format(L"%%.%dlf", m_Precision);

    for(_u32 i = 1; i < Ticks; i++)
    {
        double TickPosition = (CurrentTick - Offset) / Range;
        TickString.Format(FormatString, CurrentTick / (pow((double) 10, (int) m_Exponent)));
        double CenteredTextPosition = TickPosition - 2.0 / AxisMainHeight;

        if(TickPosition < (m_NiceMax - Offset) / Range - 25.0 / AxisMainHeight)
        {
            // Draw tick string
            drawText(TickString, OGL_DRAWABLE_FONT_ARIAL_10, 10, CenteredTextPosition, 0, 0, 0);

            // Draw grid
            if(EnableGrid)
            {
                glEnable(GL_LINE_STIPPLE);
                glLineStipple(1, 0xAAAA);
                glBegin(GL_LINES);
                glVertex2d(AxisMainWidth, TickPosition);
                glVertex2d(m_Layout.m_ControlRect.Width() - 2 * m_Layout.m_BorderSize, TickPosition);
                glEnd();
                glDisable(GL_LINE_STIPPLE);
            }

            // Draw tick line
            glBegin(GL_LINE_STRIP);
            glVertex2d(AxisMainWidth, TickPosition);
            glVertex2d(AxisMainWidth - 7, TickPosition);
            glEnd();
        }

        CurrentTick += m_TickSpacing;
    }
}



/**
*  @brief Draws the m_Exponent field in the upper left corner
*  @param m_Layout.m_BorderSize  - Size of the border
*  @param m_Layout.m_TitleHeight   - Size of the title area
*  @param m_Layout.m_XAxisHeight - Height of the X-Axis area
*  @param m_Exponent    - m_Exponent of the shown numbers
*/
void _YAxis::drawExponent()
{
    glColor3i(0, 0, 0);
    _u32 AxisMainWidth = m_BaseSize + m_Precision * 5;
    _u32 AxisMainHeight = m_Layout.m_ControlRect.Height() - m_Layout.m_XAxisHeight - 2 * m_Layout.m_BorderSize -
                          m_Layout.m_TitleHeight;
    glViewport(m_Layout.m_BorderSize + AxisTitleSize,       // X0
               m_Layout.m_BorderSize + m_Layout.m_XAxisHeight,     // Y0
               AxisMainWidth,                                      // Width
               AxisMainHeight                                      // Height
              );
    glLoadIdentity();
    gluOrtho2D(0, AxisMainWidth, 0, AxisMainHeight);
    // Draw the m_Exponentstring
    CStringW m_ExponentString = L"";
    m_ExponentString.Format(L"E%d", m_Exponent);
    drawText(m_ExponentString, OGL_DRAWABLE_FONT_ARIAL_10, 10, AxisMainHeight - 10, 0, 0, 0);
}


/**
*  @brief Calculates the width of the axis
*/
void _YAxis::calculateWidth()
{
    m_Width = m_BaseSize + AxisTitleSize + m_Precision * 5;
}

/*
*  @brief  Returns the total width
*  @return Complete width of the axis area
*/
_u32 _YAxis::getWidth()
{
    return m_Width;
}

bool _YAxis::isInsideAxis(CPoint& Point)
{
    if(Point.y > (LONG)(m_Layout.m_TitleHeight + m_Layout.m_BorderSize) &&
       Point.y < (LONG)(m_Layout.m_ControlRect.bottom - (m_Layout.m_TitleHeight + m_Layout.m_BorderSize)) &&
       Point.x > (LONG)(m_Layout.m_BorderSize) &&
       Point.x < (LONG)(m_Layout.m_YAxisWidth))
    {
        return true;
    }
    else
    {
        return false;
    }
}



/**
*  @brief Draws axis
*  @param m_Layout.m_BorderSize  - Size of the border
*  @param Min         - Minimum value on axis
*  @param Max         - Maximum value of the axis
*/
void _YAxis::draw(double Min, double Max, bool EnableGrid)
{
    // Check if title is visible
    if(m_Title != L"")
    {
        m_TitleVisible = true;
    }
    else
    {
        m_TitleVisible = false;
    }

    // Draw axis background
    drawBackground();
    drawTitle();
    drawTicks(EnableGrid);
    drawExponent();
}

void _YAxis::calculateLayout(double Min, double Max, _u32 m_XAxisHeight, _u32 m_TitleHeight)
{
    // Calculates m_NiceMin and m_NiceMax and maximum ticks
    NiceScale(Min, Max);
    setMaxTicks(15);
    // Compute m_Exponent and m_Precision
    m_Exponent = getExponent(m_NiceMin, m_NiceMax);
    m_Precision = getPrecision(m_Exponent);

    // Prevent overlapping of tick strings
    while(m_MaxTicks * 20 > m_Layout.m_ControlRect.Height() - m_Layout.m_TitleHeight - m_Layout.m_XAxisHeight)
    {
        setMaxTicks(m_MaxTicks - 1);
        NiceScale(Min, Max);
        m_Exponent = getExponent(m_NiceMin, m_NiceMax);
        m_Precision = getPrecision(m_Exponent);
    }

    // Safety checks
    if(m_Precision > 20)
    {
        m_Precision = 20;
    }

    if(m_Precision < 0)
    {
        m_Precision = 0;
    }

    m_FirstTickPosition = m_NiceMin;

    if(abs(m_FirstTickPosition - m_LastMin) <= m_TickSpacing)
    {
        m_FirstTickPosition = m_LastMin;
        m_LastMin = m_NiceMin;
        m_LastMax = m_NiceMax;
        m_NiceMin = Min;
        m_NiceMax = Max;
    }
    else
    {
        m_LastMin = m_NiceMin;
        m_LastMax = m_NiceMax;
    }

    // Make width available for others
    calculateWidth();
}